package br.edu.ufcg.splab.techniques;



import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.core.InterfaceVertex;
import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.techniques.reduction.functions.similarity.DistanceFunction;



public class Similarity implements DistanceFunction{

	
	
	/**
	 * 
	 * matrizOfSimilarity is the matrix with the percentage 
	 * of similarity between two test cases, where line is one test case and  
	 * column is another test case 
	 * 
	 */
	private double[][] matrixOfSimilaridade;
	
	/**
	 * testSuite is a list with the test cases
	 */
	private TestSuite testSuite;
	
	
	/**
	 * listRemove is a list with the test cases removed
	 * 
	 */
	private TestSuite listRemove;

	
	/**
	 * 
	 * Responsible for filling the matrix
	 * 
	 * @param TestSuite listaDeGeracao
	 */
	private void createMatrixOfSimilarity(TestSuite testSuite) {
		this.testSuite = testSuite;
		this.matrixOfSimilaridade = new double[testSuite.size()][testSuite.size()];
		this.listRemove = new TestSuite();
		zerarMatrizDeSimilaridade(this.matrixOfSimilaridade);

		for (int i = 0; i < testSuite.size(); i++) {
			for (int j = i + 1; j < testSuite.size(); j++) {
				
				//double numElementosIguais = qtdElemIguais(testSuite.get(i),testSuite.get(j));
				//double tamanho = testSuite.get(i).size()+ testSuite.get(j).size();

				this.matrixOfSimilaridade[i][j] = getSimilarity(testSuite.get(i), testSuite.get(j));//((numElementosIguais) / (tamanho / 2.0));
			}
		}
	}

	
	/**
	 * 
	 * Calculates number of elements equal the two lists
	 * 
	 * @param lista1
	 * @param lista2
	 * @return number of elements equal the two lists
	 */
	//me da a quantidade de elementos iguais entre dois casos de teste
	private double qtdElements(TestCase lista1, TestCase lista2) {
		int cont = 0;
		for (int i = 0; i < lista1.size(); i++) {
			for (int j = 0; j < lista2.size(); j++) {
				
				InterfaceEdge aresta1 = lista1.get(i);
				InterfaceEdge aresta2 = lista2.get(j);
				
				InterfaceVertex fromA1 = aresta1.getFrom();
				InterfaceVertex toA1 = aresta1.getTo();
				
				InterfaceVertex fromA2 = aresta2.getFrom();
				InterfaceVertex toA2 = aresta2.getTo();
				
				if (aresta1.getLabel().equals(aresta2.getLabel()) && 
						fromA1.getLabel().equals(fromA2.getLabel()) &&
								toA1.getLabel().equals(toA2.getLabel())  ) {
					cont++;
				}
			}
		}
		return cont;
	}

	
	
	/**
	 * 
	 * Eliminates the lowest test case, if they have 
	 * the same size it will be done randomly
	 * 
	 */
	//remove testes similares, sempre eliminamos o menor caso de teste
	//se forem de iguais tamanhos sera selecionado randomicamente
	private void removeTestsSimilar() {

		double[] coordenadas = getPositionHigher();
//		System.out.println("\n" 
//		+ "(t" + coordenadas[0] + " , " 
//		+ "t"+ coordenadas[1] + ")" 
//		+ " " + coordenadas[2]  +"aqui");

		int tamanhoLinha = this.testSuite.get((int) coordenadas[0]).size();
		int tamanhoColuna = this.testSuite.get((int) coordenadas[1]).size();
//		System.out.println(tamanhoLinha + " " + tamanhoColuna);
		if (tamanhoLinha == tamanhoColuna) {
//			System.out.println("(t" + coordenadas[0] + "==" + "t"
//					+ coordenadas[1] + ")");
			int t = (int) (Math.random() * 2);
//			System.out.println(t);
			fillsLess((int) coordenadas[t]);
			
			this.listRemove.add(this.testSuite.get((int)coordenadas[t]));
			//--this.listaDeGeracao.get((int)coordenadas[t]).clear();////////////////////////////////////////////
			//this.listaDeGeracao.remove((int)coordenadas[t]);////////////////////////////////////////////
			
			
		} else if (tamanhoLinha > tamanhoColuna) {// deleta Tcoluna
//			System.out.println("(t" + coordenadas[0] + ">" + "t"
//					+ coordenadas[1] + ")");
//			System.out.println(tamanhoLinha + "," + tamanhoColuna);

			fillsLess((int) coordenadas[1]);
			
			this.listRemove.add(this.testSuite.get((int)coordenadas[1]));
			//--this.listaDeGeracao.get((int)coordenadas[1]).clear();/////////////////////////////////////////
			//this.listaDeGeracao.remove((int)coordenadas[1]);/////////////////////////////////////////

		} else if (tamanhoColuna > tamanhoLinha) {// deleta Tlinha
//			System.out.println("(t" + coordenadas[0] + "<" + "t"
//					+ coordenadas[1] + ")");

			fillsLess((int) coordenadas[0]);
			
			this.listRemove.add(this.testSuite.get((int)coordenadas[0]));
			//--this.listaDeGeracao.get((int)coordenadas[0]).clear();///////////////////////
			//this.listaDeGeracao.remove((int)coordenadas[0]);///////////////////////
			

		}
//		sysoMatrizDeSimilaridade();
	}

	
	/**
	 * 
	 * In this algorithms removed (1-percent) of test cases
	 * 
	 * @param percent
	 */
	public void removeNtestsSimilar(double percent) {
		int qtdElimina = (int) (this.matrixOfSimilaridade.length * percent);
		for (int i = qtdElimina; i > 0; i--) {
			removeTestsSimilar();

		}
	}

	
	/**
	 * 
	 * Eliminates the row and column of the matrix,
	 * fills with less line and column
	 */
	//preenche com menos a linha e a coluna referente ao teste eliminado
	private void fillsLess(int linha) {
		for (int i = 0; i < this.matrixOfSimilaridade[linha].length; i++) {
			this.matrixOfSimilaridade[linha][i] = -1;
			this.matrixOfSimilaridade[i][linha] = -1;
		}
	}

	
	/**
	 * Responsible for returning the position of greatest similarity of matrix
	 * @return (i,j) cordinates of matrix
	 */
	//me retorna a posicao (i,j) dos casos com maior similaridade
	private double[] getPositionHigher() {
		int linha = 0;
		int coluna = 0;
		//////////////////////
		double maior = -99;
		for (int i = 0; i < this.matrixOfSimilaridade.length; i++) {
			for (int j = i + 1; j < this.matrixOfSimilaridade[i].length; j++) {
				if (this.matrixOfSimilaridade[i][j] > maior) {
					maior = this.matrixOfSimilaridade[i][j];

					linha = i;
					coluna = j;
				}
			}
		}
		double[] coordenadas = { linha, coluna, maior };
		return coordenadas;

	}

	//zerar a matriz
	private void zerarMatrizDeSimilaridade(double[][] matriz) {

		for (int i = 0; i < matriz.length; i++) {
			for (int j = 0; j < matriz.length; j++) {
				matriz[i][j] = 0.0;
			}
		}

	}

	public void sysoMatrizDeSimilaridade() {

		for (int i = 0; i < this.matrixOfSimilaridade.length; i++) {
			for (int j = 0; j < this.matrixOfSimilaridade[i].length; j++) {
//				System.out.print(this.matrixOfSimilaridade[i][j] + " ");
			}
//			System.out.println("");
		}
	}
//	public void sysoListaTestes(){
//		for(int i=0;i<this.listaDeGeracao.size();i++){
//			for(int j=0;j<this.listaDeGeracao.get(i).size();j++){
//				System.out.print(this.listaDeGeracao.get(i).get(j).getLabel());
//			}
//			System.out.println();
//		}System.out.println();
//		
//	}
//	
//	public void getRemovidos(){
//		System.out.println(this.listaRemovidos.toString());
//	}
	
	
	/**
	 * 
	 * 
	 * @param testSuite is a ArrayList of TestCase
	 * @param percent is the percentage of test cases that a testSuite must have after
	 * excution of the algorithm of similarity
	 */
	public TestSuite executeSimilarity(TestSuite testSuite,double percent){
		createMatrixOfSimilarity(testSuite);
		sysoMatrizDeSimilaridade();
		removeNtestsSimilar(percent);
//		System.out.println("KKKKKKKKKKKKKK");
//		System.out.println(this.listaDeGeracao.size());
//		for(int i =0;i<=listaDeGeracao.size();i++){
//			if(listaDeGeracao.get(i).size()==0){
//				this.listaDeGeracao.remove(i);
//			}
//		}
//		System.out.println("REMOVIDO... "+this.listaDeGeracao.size());
		
		for(TestCase t : listRemove){
			if(testSuite.contains(t)){
				testSuite.remove(t);
			}
		}
		
		return this.testSuite;
		
	}

	/**
	 * 
	 * Responsible for calculating the similarity between test cases
	 * 
	 * 
	 */
	@Override
	public double getSimilarity(TestCase path1, TestCase path2) {

		double numElementosIguais = qtdElements(path1, path2);
		double tamanho = path1.size() + path2.size();

		return ((numElementosIguais) / (tamanho / 2.0));
	}


	@Override
	public String getFunctionName() {

		return "Similarity";
	}

//	public static void main(String[] args) throws Exception {
//		ReadTGF leitor = new ReadTGF();
//		Graph grafo = leitor.getGraph("entrada4.tgf");
//
//		TestCaseGenerator t = new TestCaseGenerator();
//		TestSuite teste = t.generate(grafo);
//
//		Similarity s = new Similarity();
////		System.out.println("aksdfasdlfkasj");
//		System.out.println(s.executeSimilarity(teste, 0.5));
//
//		// s.getRemovidos();
//
//	}

	
}
